/***
Copyright (c) 2013 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may
not use this file except in compliance with the License. You may obtain
a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package com.commonsware.cwac.provider;
import android.content.ContentValues;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.util.Log;
import android.webkit.MimeTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* StreamStrategy (partial) implementation that is designed for
* use by things that need a pipe to transfer over the content.
*
* The current implementation is limited to read-only content
* (assets, raw resources, etc.). It could be augmented to support
* read-write content if/when needed.
*/
public abstract class AbstractPipeStrategy implements StreamStrategy {
/**
* @param uri the Uri of the content
* @return an InputStream on that content
* @throws IOException
*/
abstract InputStream getInputStream(Uri uri) throws IOException;
/**
* {@inheritDoc}
*/
@Override
public String getType(Uri uri) {
return(MimeTypeMap.getSingleton().getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(uri.toString())));
}
/**
* {@inheritDoc}
*/
@Override
public boolean canInsert(Uri uri) {
return(false);
}
/**
* {@inheritDoc}
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException("Um, this should not have been called");
}
/**
* {@inheritDoc}
*/
@Override
public boolean canUpdate(Uri uri) {
return(false);
}
/**
* {@inheritDoc}
*/
@Override
public int update(Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
throw new UnsupportedOperationException("Um, this should not have been called");
}
/**
* {@inheritDoc}
*/
@Override
public boolean canDelete(Uri uri) {
return(false);
}
/**
* {@inheritDoc}
*/
@Override
public void delete(Uri uri) {
throw new UnsupportedOperationException("Cannot delete a stream");
}
/**
* {@inheritDoc}
*/
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
if ("r".equals(mode)) {
ParcelFileDescriptor[] pipe=null;
try {
pipe=ParcelFileDescriptor.createPipe();
new TransferOutThread(getInputStream(uri),
new AutoCloseOutputStream(pipe[1])).start();
}
catch (IOException e) {
Log.e(getClass().getSimpleName(), "Exception opening pipe", e);
throw new FileNotFoundException("Could not open pipe for: "
+ uri.toString());
}
return(pipe[0]);
}
throw new IllegalArgumentException("Cannot support writing!");
}
/**
* {@inheritDoc}
*/
@Override
public String getName(Uri uri) {
return(uri.getLastPathSegment());
}
/**
* {@inheritDoc}
*/
@Override
public long getLength(Uri uri) {
return(-1);
}
/**
* {@inheritDoc}
*/
@Override
public boolean buildUriForFile(Uri.Builder b, File file) {
return(false);
}
/**
* Thread used to copy the InputStream contents from
* getInputStream() to an OutputStream on the pipe, to transfer
* that data to the client of this provider.
*/
static class TransferOutThread extends Thread {
InputStream in;
OutputStream out;
byte[] buf=new byte[16384];
TransferOutThread(InputStream in, OutputStream out) {
this.in=in;
this.out=out;
}
@Override
public void run() {
int len;
try {
while ((len=in.read(buf)) >= 0) {
out.write(buf, 0, len);
}
in.close();
out.close();
}
catch (IOException e) {
Log.e(getClass().getSimpleName(),
"Exception transferring file", e);
}
}
}
}